package org.bluedream.comm.fill;

import org.bluedream.comm.fill.annotation.TargetField;
import org.bluedream.comm.utils.CreateGUIDUtil;
import org.bluedream.comm.utils.EmptyUtil;
import org.bluedream.comm.utils.ReflexUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 实体类 公共字段 填充器
 * TargetField 注解说明：
 * fill：确定何时 加载填充器。取值类型：FillType 枚举值:DEFAULT\INSERT\UPDATE\INSERT_UPDATE , DEFAULT时，插入时有效。
 * value：
 *       1.属性为 Timestamp 类型时，value值无效，填充器自动取当前时间。boolean、Boolean时，传入 字符串true或者false。字符串类型直接传值即可
 *       2.id字段，系统自动生成id，如需手工传入，请重写GoalFieldFill类中的setEntity方法。
 *       3.默认 填充器会自动处理 String、Timestamp、boolean、Boolean数据，其他类型需要重写 GoalFieldFill类中的setEntity方法。
 * fillMethod：定义 填充器 调用的set方法名
 * @param <T>
 */
public class GoalFieldFill<T> {
    private List<Field> fieldList = new ArrayList<>() ;
    private List<Method> methods = new ArrayList<>();
    private List<AnnotationAnalysis> annotationAnalyses = new ArrayList<>();
    private boolean isNewRecord = false;

    /**
     * Test
     * @param args
     * @throws Exception
     */
/*    public static void main(String[] args) throws Exception {
        EntityFill ef = new EntityFill();
        BaseEntity be = new BaseEntity();
        ef.fillUpdate(be);
    }*/

    /**
     * Insert 填充器
     * @param entity
     * @return
     * @throws Exception
     */
    public T fillInsert(T entity) throws Exception{
        setAnnotationAnalyses(entity);
        setInsertEntity(entity);
        return entity;
    }

    /**
     * Update 填充器
     * @param entity
     * @return
     * @throws Exception
     */
    public T fillUpdate(T entity) throws Exception{
        setAnnotationAnalyses(entity);
        setUpdateEntity(entity);
        return entity;
    }

    /**
     * Insert 注入：INSERT 、 INSERT_UPDATE 时插入相关属性
     * @param entity
     * @throws Exception
     */
    private void setInsertEntity(T entity) throws Exception {
        Method m;
        for (AnnotationAnalysis f1:this.annotationAnalyses
             ) {
            if ("DEFAULT".equals(f1.getFillType().toString()) || "INSERT".equals(f1.getFillType().toString()) || "INSERT_UPDATE".equals(f1.getFillType().toString()))
            setEntity(entity, f1);
        }
    }

    /**
     * Update 注入：UPDATE、INSERT_UPDATE 时插入相关属性
     * @param entity
     * @throws Exception
     */
    private void setUpdateEntity(T entity) throws Exception{
        Method m;
        for (AnnotationAnalysis f1:this.annotationAnalyses
        ) {
            if ("UPDATE".equals(f1.getFillType().toString()) || "INSERT_UPDATE".equals(f1.getFillType().toString()))
            setEntity(entity, f1);
        }
    }

    /**
     * 填充器 基础方法 允许重写此方法
     * @param entity
     * @param f1        填充器解析
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    public void setEntity(T entity, AnnotationAnalysis f1) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Method m;
        String fillType = f1.getFillType().toString();
        String fieldType = f1.getFieldType().toString();
        boolean isField = isField(f1.getFieldName() , entity);
        boolean isMethod = isMethod(f1.getFillMethodName() , entity);
        if (isField && isMethod){
            m = entity.getClass().getMethod(f1.getFillMethodName() , f1.getFieldType());
            if ("class java.util.Date".equals(fieldType)){
                Timestamp nowTime = new Timestamp(System.currentTimeMillis());
                m.invoke(entity , nowTime);
            }else if ("boolean".equals(fieldType) || "class java.lang.Boolean".equals(fieldType)){
                m.invoke(entity , Boolean.parseBoolean(f1.getFillValue()));
            }
            else if ("id".equals(f1.getFieldName())){
                if (EmptyUtil.isEmpty(ReflexUtil.getMethodValue(entity , "getId"))){
                    m.invoke(entity , CreateGUIDUtil.GenerateGUID());
                }
            }else {
                m.invoke(entity , f1.getFillValue());
            }
        }
    }

    /**
     * 根据获取到的含有 TargetField 注解的属性，初始化 AnnotationAnalysis 信息 ，并添加到 List
     * @param entity
     */
    private void setAnnotationAnalyses(T entity){
        try {
            //获取类中所有属性
            this.fieldList = getObjectFields(entity);
            for (Field f1:fieldList
            ) {
                TargetField targetField = f1.getAnnotation(TargetField.class);
                if (EmptyUtil.isNoEmpty(targetField)){
                    AnnotationAnalysis annotationAnalysis = new AnnotationAnalysis(f1.getName() , f1.getType() , targetField.fill() , targetField.fillMethod() , targetField.value());
                    this.annotationAnalyses.add(annotationAnalysis);
                }
            }
        }catch (Exception e1){
            e1.printStackTrace();
        }
    }

    /**
     * 返回 entity 对象中的 isNewRecord 属性值
     * @param entity
     * @return
     * @throws Exception
     */
    public boolean getIsNewRecord(T entity) throws Exception {
        Method m;
        boolean newRecordIsExist = isMethod("getIsNewRecord" , entity);
        if (newRecordIsExist){
            m = entity.getClass().getMethod("getIsNewRecord");
            isNewRecord = (boolean)m.invoke(entity);
        }
        return isNewRecord;
    }

    /**
     * 返回 entity 对象的所有属性，包含父类
     * @param entity
     * @return
     */
    private List<Field> getObjectFields(T entity) throws NullPointerException{
        Class clazz = entity.getClass();
        while (clazz != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            fieldList.addAll(Arrays.asList(clazz .getDeclaredFields()));
            clazz = clazz.getSuperclass(); //得到父类,然后赋给自己
        }
        return fieldList;
    }

    /**
     * 返回 字段的 type信息
     * @param fieldName
     * @param entity
     * @return
     * @throws Exception
     */
    private String getFieldType(String fieldName , T entity) throws Exception{
        Class clazz = entity.getClass();
        return clazz.getDeclaredField(fieldName).getType().toString();
    }

    /**
     * 判断 Class entity 是否存在名称为 fieldName 的属性
     * @param fieldName
     * @param entity
     * @return
     * @throws NullPointerException
     */
    private Boolean isField(String fieldName , T entity) throws NullPointerException{
        fieldList = getObjectFields(entity);
        for (Field f1:fieldList
        ) {
            if (fieldName.equals(f1.getName()))
                return true;
        }
        return false;
    }

    /**
     * 返回 entity 对象中的所有方法，包含父类
     * @param entity
     * @return
     * @throws NullPointerException
     */
    private List<Method> getObjectMethods(T entity) throws NullPointerException{
        Class clazz = entity.getClass();
        while (clazz != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            methods.addAll(Arrays.asList(clazz .getDeclaredMethods()));
            clazz = clazz.getSuperclass(); //得到父类,然后赋给自己
        }
        return methods;
    }

    /**
     * 判断 Class entity 是否存在名称为 methodName 的方法
     * @param methodName
     * @param entity
     * @return
     * @throws NullPointerException
     */
    private Boolean isMethod(String methodName , T entity) throws NullPointerException{
        methods = getObjectMethods(entity);
        for (Method m1:methods
        ) {
            if (methodName.equals(m1.getName()))
                return true;
        }
        return false;
    }
}
